#pragma once

#include <array>
#include <atomic>
#include <cstddef>
#include <vector>
#include <type_traits>
#include <iostream>

template <typename T>
class RingBuffer {
 public:
  RingBuffer()
      : is_inited_(false),
        is_quit_(false),
        read_index_(0),
        write_index_(0),
        idle_write_index_(0),
        capacity_(0),
        mask_(0) {}

  ~RingBuffer() { UnInit(); }
  void Init(size_t capacity);
  void UnInit();
  size_t Size();
  bool Push(T data);
  T Pop();
  bool Full();
  void Reset();

 private:
  bool is_inited_;
  volatile bool is_quit_;
  std::atomic<size_t> read_index_;
  std::atomic<size_t> write_index_;
  std::atomic<size_t> idle_write_index_;
  size_t capacity_;
  size_t mask_;
  std::vector<T> data_queue_;
};


template <typename T>
void RingBuffer<T>::Init(size_t capacity) {
  capacity_ = capacity;
  data_queue_.resize(capacity);
  is_inited_ = true;
  is_quit_ = false;
}

template <typename T>
void RingBuffer<T>::UnInit() {
  if (is_inited_) {
    data_queue_.clear();
    read_index_ = 0;
    write_index_ = 0;
    idle_write_index_ = 0;
    capacity_ = 0;
    is_quit_ = true;
    is_inited_ = false;
  }
}

template <typename T>
size_t RingBuffer<T>::Size() {
  size_t curr_read_index = read_index_.load(std::memory_order_acquire);
  size_t curr_write_index = write_index_.load(std::memory_order_acquire);
  if (curr_read_index >= curr_write_index) {
    return 0;
  }
  return curr_write_index - curr_read_index;
}

template <typename T>
bool RingBuffer<T>::Full() {
  size_t curr_write_index = write_index_.load(std::memory_order_acquire);
  if (curr_write_index >= capacity_) {
    return true;
  } else {
    return false;
  }
}

template <typename T>
bool RingBuffer<T>::Push(T data) {
  size_t curr_write_index = 0;
  curr_write_index = write_index_.fetch_add(1, std::memory_order_acquire);
  if (curr_write_index >= capacity_) {
    return false;
  }
  data_queue_[curr_write_index] = std::move(data);
  return true;
}

template <typename T>
T RingBuffer<T>::Pop() {
  if (!is_inited_) {
    return nullptr;
  }
  size_t curr_read_index = read_index_.fetch_add(1, std::memory_order_acquire);
  size_t curr_write_index = write_index_.load(std::memory_order_acquire);
  if (curr_read_index >= curr_write_index || curr_read_index >= capacity_) {
    return nullptr;
  }
  T data = std::move(data_queue_[curr_read_index]);
  return data;
}

template <typename T>
void RingBuffer<T>::Reset() {
  write_index_ = 0;
  read_index_ = 0;
}